/* 
 * File:   CFiltering.cpp
 * Author: andrea
 * 
 * Created on November 5, 2013, 9:53 PM
 */

#include "../Headers/includes.h"
#include "CFiltering.h"

using namespace std;
using namespace cv;

CFiltering::CFiltering() {
}

/**
 * Bound filters srcImg an writes the filtered image in dstImg. Source and destination image
 * must have the same size.
 * @param threshold
 * @param rgbColor Vec3b (B,G,R)
 * @param srcImg
 * @param dstImg
 */

void CFiltering::thresholdFilteringBGR(int threshold, Vec3b rgbColor, Mat srcImg, Mat dstImg) {
    try {
        if (srcImg.rows != dstImg.rows || srcImg.cols != dstImg.cols) {
            throw (CUSERR_SRC_DST_SIZES_NOT_MATCHING);
        }
        // vectors to store lower bounds and upper bounds
        Vec3b LBs, UBs;


        // compute upper and lower bounds
        for (int i = 0; i < 3; i++) {
            if (rgbColor[i] - threshold < 0)
                LBs[i] = 0;
            else LBs[i] = rgbColor[i] - threshold;

            if (rgbColor[i] + threshold > 255)
                UBs[i] = 255;
            else UBs[i] = rgbColor[i] + threshold;
        }


        // filter the image
        for (int i = 0; i < srcImg.rows; i++) {
            for (int j = 0; j < srcImg.cols; j++) {
                int blue = srcImg.at<Vec3b>(i, j)[0];
                int green = srcImg.at<Vec3b>(i, j)[1];
                int red = srcImg.at<Vec3b>(i, j)[2];
                if ((blue >= UBs[0] || blue <= LBs[0]) || (green >= UBs[1] || green <= LBs[1]) || (red >= UBs[2] || red <= LBs[2])) {
                    dstImg.at<Vec3b>(i, j)[0] = 0;
                    dstImg.at<Vec3b>(i, j)[1] = 0;
                    dstImg.at<Vec3b>(i, j)[2] = 0;
                } else {
                    dstImg.at<Vec3b>(i, j)[0] = blue;
                    dstImg.at<Vec3b>(i, j)[1] = green;
                    dstImg.at<Vec3b>(i, j)[2] = red;
                }
            }
        }
        return;
    } catch (int e) {
        CErrors err;
        cout << err.getErrorMessage(e) << endl;
    }
}

/**
 * Bound filters srcImg an writes the filtered image in dstImg. Source and destination image
 * must have the same size.
 * @param threshold
 * @param channel char the channel on which apply the filter. Can be {'H','S','B'} case sensitive. 'B' means "both"
 * @param hsvColor Vec2b the "color" that should be isolated
 * @param srcImg Mat BGR encoded
 * @param dstImg Mat BGR encoded
 */
void CFiltering::thresholdFilteringHSV(int threshold, char channel, Vec2b hsvColor, Mat srcImg, Mat dstImg) {
    try {
        if (srcImg.rows != dstImg.rows || srcImg.cols != dstImg.cols) {
            throw (CUSERR_SRC_DST_SIZES_NOT_MATCHING);
        }
        if (channel != 'H' && channel != 'S' && channel != 'B') {
            throw (CUSERR_WRONG_CHANNEL_NAME);
        }
        int hUB;
        int hLB;
        int sUB;
        int sLB;

        // convert image color space
        cvtColor(srcImg, dstImg, CV_BGR2HSV);

        // Get HUE bounds
        if (channel == 'H' || channel == 'B') {
            if (hsvColor[0] - threshold < 0)
                hLB = 0;
            else hLB = hsvColor[0] - threshold;

            if (hsvColor[0] + threshold > 255)
                hUB = 255;
            else hUB = hsvColor[0] + threshold;
        }


        // Get Saturation bounds
        if (channel == 'S' || channel == 'B') {
            if (hsvColor[1] - threshold < 0)
                sLB = 0;
            else sLB = hsvColor[1] - threshold;

            if (hsvColor[1] + threshold > 255)
                sUB = 255;
            else sUB = hsvColor[1] + threshold;
        }

        cout << "colorH:" << hsvColor[0] << " colorS:" << hsvColor[1] << endl;
        cout << "hUb:" << hUB << " hLB:" << hLB << endl;
        cout << "sUb:" << sUB << " sLB:" << sLB << endl;

        // filter the image
        for (int i = 0; i < srcImg.rows; i++) {
            for (int j = 0; j < srcImg.cols; j++) {
                int hue = srcImg.at<Vec3b>(i, j)[0];
                int saturation = srcImg.at<Vec3b>(i, j)[1];
                int value = srcImg.at<Vec3b>(i, j)[2];

                

                if ((channel == 'H' || channel == 'B') && (hue >= hLB && hue <= hUB)) {
                    dstImg.at<Vec3b>(i, j)[0] = hue;
                 //   dstImg.at<Vec3b>(i, j)[1] = saturation;
                 //   dstImg.at<Vec3b>(i, j)[2] = value;

                }
                if ((channel == 'S' || channel == 'B') && (saturation >= hLB && saturation <= hUB)) {
                    //dstImg.at<Vec3b>(i, j)[0] = hue;
                    dstImg.at<Vec3b>(i, j)[1] = saturation;
                    //dstImg.at<Vec3b>(i, j)[2] = value;
                }

            }
        }
        return;
    } catch (int e) {
        CErrors err;
        cout << err.getErrorMessage(e) << endl;
    }
}

/**
 * Saturates an BV encoded image and puts the saturated image in dstImg.
 * Source and Destination images must have the same size.
 * @param saturationLevel int the saturation level.
 * @param srcImg Mat BGR encoded
 * @param dstImg Mat BGR encoded
 * @param channel char the channel to be saturated. Can be {'H','S'} case sensitive
 */
void CFiltering::saturateHSV(int saturationLevel, Mat srcImg, Mat dstImg, char channel) {
    // Convert to BV
    cvtColor(srcImg, dstImg, CV_BGR2HSV);
    try {
        if (srcImg.rows != dstImg.rows || srcImg.cols != dstImg.cols) {
            throw (CUSERR_SRC_DST_SIZES_NOT_MATCHING);
        }
        if (channel != 'H' && channel != 'S') {
            throw (CUSERR_WRONG_CHANNEL_NAME);
        }
        if (saturationLevel > 255 || saturationLevel < 0) {
            throw (CUSERR_WRONG_SATURATION_LEVEL);
        }

        //Saturated Image
        for (int i = 0; i < dstImg.rows; i++) {
            for (int j = 0; j < dstImg.cols; j++) {
                if (channel == 'H')
                    dstImg.at<Vec3b>(i, j)[0] = saturationLevel;
                if (channel == 'S')
                    dstImg.at<Vec3b>(i, j)[1] = saturationLevel;
                dstImg.at<Vec3b>(i, j)[2] = saturationLevel;

            }
        }
    } catch (int e) {
        CErrors err;
        cout << err.getErrorMessage(e) << endl;
    }
}